﻿using Microsoft.Extensions.DependencyInjection;
using Sgr.Caching;
using Sgr.Caching.Services;
using Sgr.Domain.Entities;
using Sgr.UPMS.Application;
using Sgr.UPMS.Application.Queries;
using Sgr.UPMS.Application.ViewModels;
using Sgr.UPMS.Domain.Menus;
using Sgr.UPMS.Domain.Roles;
using Sgr.UPMS.Domain.Users;
using Sgr.Utilities;

namespace Sgr.UPMS.Infrastructure.Queries
{
    /// <summary>
    /// 用户权限查询服务实现类
    /// 该类负责:
    /// 1. 查询用户权限信息
    /// 2. 加载用户可访问的菜单树
    /// 3. 实现权限和菜单的缓存管理
    /// </summary>
    public class UserPermissionQueries : IUserPermissionQueries
    {
        /// <summary>
        /// 查询用户角色时需要包含的导航属性
        /// </summary>
        private static readonly string[] UserRolesIncludeProperties = { "Roles" };

        /// <summary>
        /// 缓存管理器
        /// </summary>
        private readonly ICacheManager _cacheManager;

        /// <summary>
        /// UPMS模块查询上下文
        /// </summary>
        private readonly IUpmsQueryContext _context;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="context">UPMS查询上下文</param>
        /// <param name="cacheManager">缓存管理器</param>
        /// <exception cref="ArgumentNullException">当参数为空时抛出</exception>
        public UserPermissionQueries(IUpmsQueryContext context, ICacheManager cacheManager)
        {
            _context = context ?? throw new ArgumentNullException(nameof(context));
            _cacheManager = cacheManager ?? throw new ArgumentNullException(nameof(cacheManager));
        }

        /// <summary>
        /// 获取用户的权限信息
        /// </summary>
        /// <param name="userId">用户标识</param>
        /// <param name="orgId">组织标识</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>用户权限信息视图模型</returns>
        /// <remarks>
        /// 该方法会从缓存中获取数据,如果缓存不存在则从数据库加载并写入缓存
        /// </remarks>
        public Task<OutUserPermissionViewModel?> GetUserPermissionAsync(
            long userId,
            long orgId,
            CancellationToken cancellationToken = default)
        {
            // 验证
            if (userId <= 0 || orgId <= 0)
                throw new ArgumentException("Invalid userId or orgId");

            return _cacheManager.GetAsync(
                CacheKeys.GetUserPermissionKey(orgId, userId),
                async () =>
                {
                    return await LoadUserPermissionsAsync(userId, cancellationToken);
                },
                CacheKeys.CreateUserPermissionCacheOptions(),
                cancellationToken);
        }

        /// <summary>
        /// 获取用户可访问的菜单树
        /// </summary>
        /// <param name="userId">用户标识</param>
        /// <param name="orgId">组织标识</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>用户菜单树视图模型集合</returns>
        /// <remarks>
        /// 该方法会:
        /// 1. 获取用户的权限信息
        /// 2. 根据权限过滤可访问的菜单
        /// 3. 构建菜单树结构
        /// </remarks>
        public async Task<IEnumerable<OutUserMenuViewModel>> GetUserMenusAsync(
            long userId,
            long orgId,
            CancellationToken cancellationToken = default)
        {
            // 验证
            if (userId <= 0 || orgId <= 0)
                throw new ArgumentException("Invalid userId or orgId");

            // 获取用户权限 (优先会从缓存中读取)
            var userPermission = await GetUserPermissionAsync(userId, orgId, cancellationToken);

            // 如果用户没有权限，返回空集合
            if (userPermission == null || userPermission.Permissions.Count == 0 || string.IsNullOrEmpty(userPermission.PermissionKey))
                return Enumerable.Empty<OutUserMenuViewModel>();

            // 查询结果
            var result = await _cacheManager.GetAsync(
                CacheKeys.GetPermissionsMenusKey(userPermission.PermissionKey),
                async () =>
                {
                    return await LoadPermissionsMenuAsync(userPermission, cancellationToken);
                },
                CacheKeys.CreatePermissionMenuCacheOptions(),
                cancellationToken);

            return result ?? Enumerable.Empty<OutUserMenuViewModel>();
        }

        /// <summary>
        /// 加载用户权限信息
        /// </summary>
        /// <param name="userId">用户标识</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>用户权限信息视图模型</returns>
        /// <remarks>
        /// 该方法会:
        /// 1. 查询用户基本信息
        /// 2. 加载用户关联的角色
        /// 3. 获取角色包含的权限资源
        /// </remarks>
        private async Task<OutUserPermissionViewModel?> LoadUserPermissionsAsync(long userId, CancellationToken cancellationToken = default)
        {
            // 构造用户查询条件
            var query = _context
              .QueryWithNavigation<User>(UserRolesIncludeProperties)
              .Where(f => f.Id == userId)
              .Where(f => !f.IsDeleted)
              .Where(f => f.State == EntityStates.Normal);

            // 查询用户信息
            var user = await _context.FirstOrDefaultAsync(query, cancellationToken);

            // 如果用户不存在，返回null
            if (user == null)
                return null;

            // 创建权限视图模型
            var result = new OutUserPermissionViewModel
            {
                UserId = user.Id,
                RealName = string.IsNullOrEmpty(user.UserName) ? user.LoginName : user.UserName,
                DepartmentId = user.DepartmentId,
                IsSuperAdmin = user.IsSuperAdmin,
                OrgId = user.OrgId,
                Permissions = new HashSet<string>()
            };

            //加载角色
            var roleIds = user.Roles.Select(s => s.RoleId).ToArray();
            if (roleIds.Length > 0)
            {
                var resourcesQuery = _context.Query<Role>()
                    .Where(r => r.State == EntityStates.Normal)
                    .Where(r => roleIds.Contains(r.Id))
                    .Select(r => r.Resources)
                    .SelectMany(resources => resources
                        .Select(res => res.ResourceCode))
                    .Distinct()
                    .OrderBy(x => x);

                var resources = (await _context.ToListAsync(resourcesQuery, cancellationToken)).ToArray();
                foreach (string resource in resources)
                {
                    result.Permissions.Add(resource);
                }

                //string joinPermissions = string.Join('-', roleIds);
                string joinPermissions = string.Join('-', resources);
                result.PermissionKey = HashHelper.CreateMd5(joinPermissions);
            }

            return result;
        }

        /// <summary>
        /// 根据用户权限加载菜单信息
        /// </summary>
        /// <param name="userPermission">用户权限信息</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>用户菜单树视图模型集合</returns>
        /// <remarks>
        /// 该方法会:
        /// 1. 从缓存加载所有菜单
        /// 2. 根据用户权限过滤可访问的菜单
        /// 3. 构建菜单树结构
        /// </remarks>
        private async Task<IEnumerable<OutUserMenuViewModel>> LoadPermissionsMenuAsync(OutUserPermissionViewModel userPermission, CancellationToken cancellationToken)
        {
            //查询所有的菜单(菜单一般不会变化，默认会缓存1天)
            var allMenus = await _cacheManager.GetAsync(
                CacheKeys.GetAllMenusKey(),
                async () =>
                {
                    return await LoadMenusAsync(cancellationToken);
                },
                CacheKeys.CreateMeunCacheOptions(),
                cancellationToken);

            if (allMenus == null || !allMenus.Any())
                return Enumerable.Empty<OutUserMenuViewModel>();

            var allMenusList = allMenus.ToList();
            // 获取所有的权限菜单ID
            var allowedMenuIds = allMenusList
                .Where(m => !string.IsNullOrEmpty(m.Permission) && userPermission.Permissions.Contains(m.Permission))
                .Select(m => m.Id)
                .ToHashSet();
            // 筛选所有的根节点
            var rootMenus = allMenusList.Where(m => m.ParentId == 0).ToList();

            return BuildUserMenusTree(rootMenus, allMenusList, allowedMenuIds);
        }

        /// <summary>
        /// 加载所有菜单信息
        /// </summary>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>菜单实体集合</returns>
        /// <remarks>
        /// 只加载:
        /// 1. 未隐藏的菜单
        /// 2. 已激活的菜单
        /// 3. 按Order字段排序
        /// </remarks>
        private async Task<IEnumerable<Menu>> LoadMenusAsync(CancellationToken cancellationToken)
        {
            var query = _context.Query<Menu>()
                            .Where(f => !f.Hidden)
                            .Where(f => f.IsActive)
                            .OrderBy(f => f.Order);
            return await _context.ToListAsync(query, cancellationToken);
        }

        /// <summary>
        /// 构建用户菜单树
        /// </summary>
        /// <param name="menus">当前层级的菜单集合</param>
        /// <param name="allMenus">所有菜单集合</param>
        /// <param name="allowedMenuIds">允许访问的菜单ID集合</param>
        /// <returns>用户菜单树视图模型列表</returns>
        /// <remarks>
        /// 该方法会:
        /// 1. 根据权限过滤菜单
        /// 2. 递归构建树形结构
        /// 3. 转换为前端所需的视图模型
        /// </remarks>
        private static List<OutUserMenuViewModel> BuildUserMenusTree(IEnumerable<Menu> menus, IList<Menu> allMenus, HashSet<long> allowedMenuIds)
        {
            var result = new List<OutUserMenuViewModel>();

            foreach (var menu in menus)
            {
                //当前菜单的子菜单
                var childrens = allMenus.Where(f => f.ParentId == menu.Id);

                // 如果当前节点或任一子节点有权限，则保留
                bool hasPermission = allowedMenuIds.Contains(menu.Id)
                    || childrens.Any(c => allowedMenuIds.Contains(c.Id));

                if (!hasPermission) continue;

                var node = new OutUserMenuViewModel
                {
                    Name = menu.RouteName ?? Guid.NewGuid().ToString("N"),
                    Path = menu.ExternalLink ? null : menu.RoutePath,
                    Component = menu.ComponentPath,
                    Redirect = menu.RedirectPath,
                    Meta = new UserMenuMeta()
                };

                node.Meta.FrameSrc = menu.ExternalLink ? menu.RoutePath : null;
                node.Meta.FrameBlank = menu.Target == TargetType.Blank ? true : null;
                node.Meta.Affix = menu.Affix;
                node.Meta.Icon = menu.Icon ?? string.Empty;
                node.Meta.Title.Add("zh_CN", menu.Name);

                node.Children = BuildUserMenusTree(childrens, allMenus, allowedMenuIds);

                if (node.Children != null && node.Children.Count == 0)
                    node.Children = null;

                result.Add(node);
            }

            return result;
        }
    }
}